#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

//宏函数，宏定义必须是单行，所以这里使用续行符
#define skipSpace(start) do{\
    while(isspace(*start))\
        ++start;\
}while(0)


char command_line[1024];//命令行数组
char* myargv[64];

int redirType = NONE_REDIR;
char* redirFile = NULL;



void CommandCheck(char* commands)
{
    assert(commands);
    char* start = commands;
    char* end = commands + strlen(commands);

    while(start < end)
    {
        //输出重定向  or 追加重定向
        if(*start == '>')
        {
            *start = '\0';
            ++start;
            if(*start == '>')
            {
                redirType = APPEND_REDIR;
                ++start;
            }
            else 
            {
                redirType = OUTPUT_REDIR;
            }
            skipSpace(start);
            redirFile = start;
            break;
        }
        else if(*start == '<')
        {
            //输入重定向
            *start = '\0';
            ++start;
            skipSpace(start);
            redirType = INPUT_REDIR;
            redirFile = start;
            break;
        }
        else 
        {
            ++start;
        }
    }
}
int main()
{
    while(1)
    {
        //对标记变量初始化，每次执行的时候都要恢复初始状态
        redirType = NONE_REDIR;
        redirFile = NULL;
        //输入提示符：
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        //获取用户输入，回车结束\n
        char* s = fgets(command_line, sizeof(command_line)-1, stdin);
        assert(s != NULL);

        command_line[strlen(command_line)-1] = 0;
        
        //检查输入的字符串，看是否需要重定向
        CommandCheck(command_line);

        //ls -a -l -i --> "ls","-a","-l","-i"
        myargv[0] = strtok(command_line," ");
        int i = 1;
        while(myargv[i++] = strtok(NULL, " "));
        if(myargv[0] && strcmp(myargv[0],"cd") == 0)
        {
            if(myargv[1])
                chdir(myargv[1]);

            continue;
        }
//条件编译，测试上面的分离字符串是否成功
#ifdef DEBUG 
        int j = 0;
        for(j  = 0; myargv[j]; ++j)
        {
            printf("argv[%d]:%s\n",j,myargv[j]);
        }
#endif

        pid_t id = fork();
        assert(id != -1);
        if(id == 0)
        {
            //进行重定向
            //通过父进程提供的信息(redirType，redirFile)，进行重定向
            switch(redirType)
            {
                case NONE_REDIR:
                    break;
                case INPUT_REDIR:
                    {
                        int fd = open(redirFile,O_RDONLY);
                        if(fd < 0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        dup2(fd,0);
                        break;
                    }
                case OUTPUT_REDIR:
                case APPEND_REDIR:
                    {
                        umask(0);
                        int flags = O_WRONLY | O_CREAT;
                        if(redirType == APPEND_REDIR)
                        {
                            flags |= O_APPEND;
                        }
                        else 
                        {
                            flags |= O_TRUNC;
                        }
                        int fd = open(redirFile, flags, 0666);
                        if(fd < 0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        dup2(fd, 1);
                        break;
                    }
                default:
                    printf("bug?\n");
                    break;
            }
            execvp(myargv[0],myargv);
            exit(1);
        }
        waitpid(id, NULL, 0);
    }
    return 0;
}
